package com.sky.service.wx_user.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.sky.config.BaiduMapProperties;
import com.sky.constant.MessageConstant;
import com.sky.context.BaseContext;
import com.sky.dto.OrdersPaymentDTO;
import com.sky.dto.OrdersSubmitDTO;
import com.sky.entity.*;
import com.sky.exception.OrderBusinessException;
import com.sky.mapper.UserMapper;
import com.sky.mapper.WXAddressBookMapper;
import com.sky.mapper.WXOrdersMapper;
import com.sky.mapper.WXShoppingCartMapper;
import com.sky.result.PageResult;
import com.sky.service.wx_user.WXAddressBookService;
import com.sky.service.wx_user.WXOrdersService;
import com.sky.service.wx_user.WXShoppingCartService;
import com.sky.utils.HttpClientUtil;
import com.sky.utils.WeChatPayUtil;
import com.sky.vo.OrderPaymentVO;
import com.sky.vo.OrderSubmitVO;
import com.sky.vo.OrderVO;
import com.sky.websocket.WebSocketServer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service
public class WXOrdersServiceImpl implements WXOrdersService {
    @Autowired
    private WXShoppingCartMapper wxShoppingCartMapper;
    @Autowired
    private WXAddressBookMapper wxAddressBookMapper;
    @Autowired
    private WXOrdersMapper wxOrdersMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private WeChatPayUtil weChatPayUtil;
    @Autowired
    private WebSocketServer webSocketServer;
    // 注入整个百度地图配置属性对象
    @Autowired
    private BaiduMapProperties baiduMapProperties;

    // === 更改点 3: 将 API 地址定义为类常量，便于维护和复用 ===
    private static final String GEO_CODING_URL = "https://api.map.baidu.com/geocoding/v3/";
    private static final String DIRECTION_URL = "https://api.map.baidu.com/directionlite/v1/riding";
    /**
     * 用户下单
     * @param dto
     * @return
     */
    @Transactional//  多表操作开启事务
    @Override
    public OrderSubmitVO submit(OrdersSubmitDTO dto) {
        //查询地址表，获取收货人信息
        AddressBook addressBook = wxAddressBookMapper.getById(dto.getAddressBookId());
        if(addressBook == null){
            throw new OrderBusinessException(MessageConstant.ADDRESS_BOOK_IS_NULL);
        }
        //查询用户表，获取用户相关信息
        Long userId = BaseContext.getCurrentId();
        User user = userMapper.selectById(userId);
        if(user == null){
            throw new OrderBusinessException(MessageConstant.ACCOUNT_NOT_FOUND);
        }
        //查询购物车列表数据--只查询当前用户的购物车数据
        List<ShoppingCart> cartList = wxShoppingCartMapper.list(userId);
        if(cartList == null || cartList.size() == 0){
            throw new OrderBusinessException(MessageConstant.SHOPPING_CART_IS_NULL);
        }
        //优化下单逻辑：需要检验配送范围（如果超过50公里，就抛出自定义异常）
        // === 更改点 4: 拼接地址字符串时，检查并处理 null 值，增强代码健壮性 ===
        String userAddress = (addressBook.getProvinceName() == null ? "" : addressBook.getProvinceName())
                + (addressBook.getCityName() == null ? "" : addressBook.getCityName())
                + (addressBook.getDistrictName() == null ? "" : addressBook.getDistrictName())
                + (addressBook.getDetail() == null ? "" : addressBook.getDetail());
        if (!checkOutOfRange(userAddress)) {
            throw new OrderBusinessException(MessageConstant.OUT_OF_RANGE);
        }

        //1.构造订单数据，存入orders表中
        //创建orders对象用于封装数据
        Orders orders = new Orders();
        //将前端传来的数据复制到orders对象中
        BeanUtils.copyProperties(dto,orders);
        //补全Orders表中剩余的数据
        orders.setNumber(System.currentTimeMillis()+"");//订单号为当前时间戳然后转为字符串
        orders.setStatus(Orders.PENDING_PAYMENT);//订单状态为待付款
        orders.setUserId(BaseContext.getCurrentId());//用户id为当前登录的用户id
        orders.setOrderTime(LocalDateTime.now());//订单下单时间
        orders.setPayStatus(Orders.UN_PAID);//订单支付状态为未支付
        //前面通过查询地址表,和用户表，获取用户信息,地址详细,然后才能将数据封装到order对象中
        orders.setPhone(addressBook.getPhone());//通过前面的查询地址表，获取收货人电话信息
        orders.setAddress(addressBook.getDetail());//通过前面的查询地址表，获取收货人地址信息
        orders.setUserName(user.getName());//通过前面的查询用户表，获取下单用户姓名
        //将上面封装好的数据插入orders表中
        wxOrdersMapper.insert(orders);//插入数据
        log.info("订单id：{}",orders.getId());
        //2.构造订单明细数据,存入order_detail表中
        //订单明细表中的数据对应购物车表数据
        List<OrderDetail> orderDetailList = new ArrayList<>();//创建集合对象因为购物车数据可能很多需要显示多条数据
        //通过循环遍历cartList(前面查询的购物车数据)购物车数据,构造出订单明细数据
        cartList.forEach(cart -> {
            OrderDetail orderDetail = new OrderDetail();//创建订单明细对象
            BeanUtils.copyProperties(cart,orderDetail,"id");//将购物车数据复制到订单明细对象中,但是注意这里要忽略id属性,因为会和order_detail表中的id属性冲突
            //关联订单id
            orderDetail.setOrderId(orders.getId());//订单id为当前订单的id
            orderDetailList.add(orderDetail);//添加到集合中
        });
        //批量插入订单明细数据
        wxOrdersMapper.insertBatch(orderDetailList);
        //3.清空购物车
        wxShoppingCartMapper.delete(userId);
        //4.构造0rderSubmitV0对象，并返回
        return OrderSubmitVO.builder()
                .id(orders.getId())
                .orderNumber(orders.getNumber())
                .orderAmount(orders.getAmount())
                .orderTime(orders.getOrderTime())
                .build();
    }
    /**
     * 校验配送范围
     */
    private boolean checkOutOfRange(String address) {
        //1.获取店铺的地址坐标
        Map<String, String> paramMap = new HashMap<>();
        // === 更改点 5: 从注入的 baiduMapProperties 对象获取商家地址和 AK ===
        paramMap.put("address", baiduMapProperties.getShopAddress());
        paramMap.put("output", "json");
        paramMap.put("ak", baiduMapProperties.getAk());
        log.info("获取店铺的地址坐标:{}", baiduMapProperties.getShopAddress());
        String jsonStr = HttpClientUtil.doGet(GEO_CODING_URL, paramMap);
        JSONObject jsonObject = JSON.parseObject(jsonStr);
        // === 更改点 6: 对 API 返回结果进行更严谨的判断，防止空指针 ===
        if (jsonObject == null || !jsonObject.getInteger("status").equals(0)) { //地址解析失败
            throw new OrderBusinessException(MessageConstant.ADDRESS_PARSE_FAILED);
        }
        JSONObject result = jsonObject.getJSONObject("result");
        if (result == null || result.getJSONObject("location") == null) {
            throw new OrderBusinessException(MessageConstant.ADDRESS_PARSE_FAILED);
        }

        JSONObject location = result.getJSONObject("location");
        String lng = location.getString("lng");   //经度
        String lat = location.getString("lat");   //维度
        String origin = lat + "," + lng;
        log.info("店铺的坐标：{}", origin);

        //2.获取收货地址的坐标
        paramMap.put("address", address);
        // === 添加日志，记录传入的收货地址 ===
        log.info("开始解析收货地址：{}", address);
        jsonStr = HttpClientUtil.doGet(GEO_CODING_URL, paramMap);
        jsonObject = JSON.parseObject(jsonStr);
        if (jsonObject == null || !jsonObject.getInteger("status").equals(0)) { //地址解析失败
            throw new OrderBusinessException(MessageConstant.ADDRESS_PARSE_FAILED);
        }
        result = jsonObject.getJSONObject("result");
        if (result == null || result.getJSONObject("location") == null) {
            throw new OrderBusinessException(MessageConstant.ADDRESS_PARSE_FAILED);
        }
        location = result.getJSONObject("location");
        lng = location.getString("lng");   //经度
        lat = location.getString("lat");   //维度
        String destination = lat + "," + lng;
        log.info("收货地址的坐标：{}", destination);

        //3.根据店铺和收货地址坐标，进行距离计算
        // === 更改点 7: 清理并重用 map，或者创建新 map，以准备路线规划API的参数 ===
        Map<String, String> directionParams = new HashMap<>();
        directionParams.put("origin", origin);
        directionParams.put("destination", destination);
        directionParams.put("ak", baiduMapProperties.getAk()); // 同样从配置对象获取 AK

        jsonStr = HttpClientUtil.doGet(DIRECTION_URL, directionParams);
        jsonObject = JSON.parseObject(jsonStr);
        if (jsonObject == null || !jsonObject.getInteger("status").equals(0)) { //路线规划失败
            throw new OrderBusinessException(MessageConstant.ADDRESS_PARSE_FAILED);
        }
        result = jsonObject.getJSONObject("result");
        if (result == null || result.getJSONArray("routes") == null || result.getJSONArray("routes").isEmpty()) {
            throw new OrderBusinessException(MessageConstant.ADDRESS_PARSE_FAILED);
        }
        JSONArray routes = result.getJSONArray("routes");
        // 获取距离，单位是米
        Integer distance = routes.getJSONObject(0).getInteger("distance");
        log.info("配送距离：{}米", distance);

        // === 更改点 8: 从注入的 baiduMapProperties 对象获取配送范围进行比较 ===
        return distance <= baiduMapProperties.getDistance();
    }



    /**
     * 订单支付
     *
     * @param ordersPaymentDTO
     * @return
     */
    public OrderPaymentVO payment(OrdersPaymentDTO ordersPaymentDTO) throws Exception {
//        // 当前登录用户id
//        Long userId = BaseContext.getCurrentId();
//        User user = userMapper.selectById(userId);
//
//        //调用微信支付接口，生成预支付交易单
//        JSONObject jsonObject = weChatPayUtil.pay(
//                ordersPaymentDTO.getOrderNumber(), //商户订单号
//                new BigDecimal(0.01), //支付金额，单位 元 正常情况需要通过查询订单接口获取，这里先写死默认0.01
//                "苍穹外卖订单", //商品描述
//                user.getOpenid() //微信用户的openid
//        );
//
//        if (jsonObject.getString("code") != null && jsonObject.getString("code").equals("ORDERPAID")) {
//            throw new OrderBusinessException("该订单已支付");
//        }
//
//        OrderPaymentVO vo = jsonObject.toJavaObject(OrderPaymentVO.class);
//        vo.setPackageStr(jsonObject.getString("package"));
//        return vo;
//
        //模拟支付成功,因为没有商户号，所以模拟支付成功,上面的是校验商户id密钥等操作所以先注释掉

        log.info("模拟支付成功");
        //业务处理，修改订单状态、来单提醒
        paySuccess(ordersPaymentDTO.getOrderNumber());
        //返回一个空对象即可
        return new OrderPaymentVO();
    }

    /**
     * 支付成功，修改订单状态
     *
     * @param outTradeNo
     */
    public void paySuccess(String outTradeNo) {
        // 当前登录用户id
        Long userId = BaseContext.getCurrentId();

        // 根据订单号查询当前用户的订单
        Orders ordersDB = wxOrdersMapper.getByNumberAndUserId(outTradeNo, userId);

        // 根据订单id更新订单的状态、支付方式、支付状态、结账时间
        Orders orders = Orders.builder()
                .id(ordersDB.getId())
                .status(Orders.TO_BE_CONFIRMED)
                .payStatus(Orders.PAID)
                .checkoutTime(LocalDateTime.now())
                .build();

        wxOrdersMapper.update(orders);

        //通过websocket服务进行消息推送下单成功推送信息进行提醒
        //  1. 创建一个Map，用于封装要发送的消息内容
        log.info("订单:{}支付成功更新订单状态:{}", ordersDB.getId(),orders);
        // 2. 构建要发送的消息内容 (一个Map)
        Map map = new HashMap();
        map.put("type", 1);         // 约定一个消息类型：1代表是来单提醒
        map.put("orderId", orders.getId());     // 附带订单ID，方便前端点击通知直接跳转
        map.put("content", "订单号：" + outTradeNo); // 具体的提示内容
        log.info("发送消息给客户端:{}", map);
        // 3. 将Map转换为JSON字符串，并调用WebSocket服务进行群发
        webSocketServer.sendToAllClient(JSON.toJSONString(map));
    }

    /**
     * 历史订单查询
     *
     * @param page
     * @param pageSize
     * @param status
     * @return
     */
    @Override
    public PageResult historyOrders(Integer page, Integer pageSize, Integer status) {
        //  分页查询
        PageHelper.startPage(page, pageSize);
        //  查询当前用户对应的订单数据
        Page<OrderVO> pageInfo = wxOrdersMapper.pageQuery(BaseContext.getCurrentId(), status);
        //  封装成分页结果对象并返回
      PageResult pageResult = new PageResult(pageInfo.getTotal(), pageInfo.getResult());
       //查询订单对应的菜品详情并将菜品数据封装到List<OrderDetail> orderDetailList集合中去
        for (OrderVO orderVO : pageInfo) {
            List<OrderDetail> orderDetailList = wxOrdersMapper.getByOrderId(orderVO.getId());
            orderVO.setOrderDetailList(orderDetailList);
        }
        //  将查询到的订单数据封装到OrderVO对象中
        pageResult.setRecords(pageInfo);
      return pageResult;
    }

    /**
     * 订单详情
     *
     * @param id
     * @return
     */
    @Override
    public OrderVO orderDetail(Long id) {
        //根据id查询订单数据
        Orders orders = wxOrdersMapper.getById(id);
        //创建OrderVO对象,将查询到的订单数据封装到OrderVO对象中
        OrderVO orderVO = new OrderVO();
        BeanUtils.copyProperties(orders,orderVO);
        //根据address_book_id查询订单对应的收货人数据,并将收货人数据封装到OrderVO中的对consignee属性中
       //获取前面查询到的address_book_id数据
        AddressBook addressBook = wxAddressBookMapper.getById(orders.getAddressBookId());
        orderVO.setConsignee(addressBook.getConsignee());
        //查询订单对应的菜品数据
        List<OrderDetail> orderDetailList = wxOrdersMapper.getByOrderId(id);
        //将菜品数据封装到OrderVO对象中
        orderVO.setOrderDetailList(orderDetailList);
        //返回
        return orderVO;
    }

    /**
     * 取消订单
     *
     * @param id
     */
    @Override
    public void cancel(Long id) throws Exception {
        //  根据id查询订单
        Orders orders = wxOrdersMapper.getById(id);
        if (orders == null) {
            //  订单不存在
            throw new OrderBusinessException(MessageConstant.ORDER_NOT_FOUND);
        }
        // 订单状态 1待付款 2待接单 3已接单 4派送中 5已完成 6已取消
        // 用户端取消订单，只能取消还未接单的订单，其他情况取消订单，需要联系商家在后台取消
        if (orders.getStatus() > 2) {
            throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);
        }
        //  封装的待更新订单数据
        Orders newOrders = new Orders();
        newOrders.setId(id);
        // 需要判断订单支付状态，订单处于待接单状态下取消，需要进行退款
        if (orders.getPayStatus().equals(Orders.PAID)) {
            //调用微信支付退款接口 --由于没有可用的商户id，下面的退款操作执行会失败，直接注释即可，假设退款成功了，直接修改状态
//             weChatPayUtil.refund(
//                     orders.getNumber(), //商户订单号
//                     orders.getNumber(), //商户退款单号
//                     new BigDecimal(0.01),//退款金额，单位 元
//                     new BigDecimal(0.01));//原订单金额
            //支付状态修改为 退款
            orders.setPayStatus(Orders.REFUND);
        }
        //更新订单状态、取消原因、取消时间
        newOrders.setStatus(Orders.CANCELLED);
        newOrders.setCancelReason("用户取消");
        newOrders.setCancelTime(LocalDateTime.now());
        wxOrdersMapper.update(newOrders);
    }

    /**
     * 再来一单
     *
     * @param id
     */
    @Override
    public void repetition(Long id) {
        // 查询当前用户id
        Long userId = BaseContext.getCurrentId();
        // 根据订单id查询当前订单详情
        List<OrderDetail> orderDetailList = wxOrdersMapper.getByOrderId(id);
        //将订单详情对象转换为购物车对象
        List<ShoppingCart> shoppingCartList = orderDetailList.stream().map(x -> {
            //  创建新的购物车对象
            ShoppingCart shoppingCart = new ShoppingCart();
            //  将订单详情对象属性复制到购物车对象中,并且排除id属性
            BeanUtils.copyProperties(x,shoppingCart,"id");
            //  设置用户id为当前用户id
            shoppingCart.setUserId(userId);
            //  设置创建时间为当前时间
            shoppingCart.setCreateTime(LocalDateTime.now());
            //  返回
            return shoppingCart;
        }).collect(Collectors.toList());//  将list集合转换为流
        //将购物车对象批量添加到数据库
        wxShoppingCartMapper.insertBatch(shoppingCartList);
    }

    /**
     * 催单
     * @param id
     */
    @Override
    public void reminder(Long id) {
        // 1. 根据ID查询订单，确保订单存在
        Orders orders = wxOrdersMapper.getById(id);
        if (orders == null) {
            throw new OrderBusinessException(MessageConstant.ORDER_NOT_FOUND);
        }
        // 2. 构建要发送的消息内容 (一个Map)
        Map map = new HashMap();
        map.put("type", 2);         // 约定一个消息类型：2代表是催单消息
        map.put("orderId", id);     // 附带订单ID，方便前端点击通知直接跳转
        map.put("content", "订单号：" + orders.getNumber()); // 具体的提示内容

        // 3. 将Map转换为JSON字符串，并调用WebSocket服务进行群发
        webSocketServer.sendToAllClient(JSON.toJSONString(map));
    }
}
